#include <kernel.h>

struct kvstore_entry_header_s
{
	 uint32_t value;
	 struct kvstore_entry_header_s *next;
};

#define HASH_SIZE 19997

struct kvstore_entry_header_s *hash[HASH_SIZE];
mutex_t hash_lock[HASH_SIZE];

int
kvstore_init(void)
{
	 int i;
	 for (i = 0; i != HASH_SIZE; ++ i)
	 {
		  hash[i] = NULL;
		  mutex_init(hash_lock + i);
	 }
}

static int
hash_func(const char *str, int len)
{
	 /* ELF HASH HERE */
	 unsigned int hash = 0;
	 unsigned int x    = 0;
	 int i;
	 for(i = 0; i < len; i++)
	 {
		  hash = (hash << 4) + str[i];
		  if((x = hash & 0xF0000000L) != 0)
		  {
			   hash ^= (x >> 24);
			   hash &= ~x;
		  }
	 }

	 return (hash & 0x7FFFFFFF);
}

int
kvstore_get(const char *name, int len, uint32_t *value)
{
	 int h = hash_func(name, len) % HASH_SIZE;
	 mutex_acquire(hash_lock + h, NULL, NULL);
	 struct kvstore_entry_header_s *cur = hash[h];
	 while (cur != NULL)
	 {
		  if (strcmp((char *)(cur + 1), name) == 0)
			   break;
		  else cur = cur->next;
	 }

	 if (cur == NULL)
	 {
		  mutex_release(hash_lock + h);
		  return -1;
	 }
	 else
	 {
		  if (value) *value = cur->value;
		  mutex_release(hash_lock + h);
		  return 0;
	 }
}

int
kvstore_set(const char *name, int len, uint32_t value)
{
	 int h = hash_func(name, len) % HASH_SIZE;
	 mutex_acquire(hash_lock + h, NULL, NULL);
	 struct kvstore_entry_header_s *cur = hash[h];
	 while (cur != NULL)
	 {
		  if (strcmp((char *)(cur + 1), name) == 0)
			   break;
		  else cur = cur->next;
	 }

	 if (cur == NULL)
	 {
		  /* XXX: resource alloc here */
		  cur = (struct kvstore_entry_header_s *)ekf_alloc(sizeof(struct kvstore_entry_header_s) + len + 1);
		  memmove(cur + 1, name, len);
		  ((char *)(cur + 1))[len] = 0;
		  
		  cur->value = value;
		  cur->next = hash[h];
		  
		  hash[h] = cur;

		  mutex_release(hash_lock + h);
		  return 0;
	 }
	 else
	 {
		  cur->value = value;

		  mutex_release(hash_lock + h);
		  return 0;
	 }
}

int
kvstore_get_cstring(const char *name, uint32_t *value)
{
	 return kvstore_get(name, strlen(name), value);
}

int
kvstore_set_cstring(const char *name, uint32_t value)
{
	 return kvstore_set(name, strlen(name), value);
}

